﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Ecopsy.Tools;
using AgencyEntityLib.BaseEntity;
using System.Web.UI.WebControls;
using System.Text;
using System.IO;
using System.Xml;
using System.Web.UI;

/// <summary>
/// Summary description for SurfaceSheduleRowWriter
/// </summary>
public class SurfaceSheduleRowWriter
{
    #region Хранилище стэка переходов поверхности из состояние в состояние
    public class PeriodItem
    {
        public DateTime FirstDay = DateTime.Now;
        public DateTime LastDay = DateTime.Now;
    }

    /// <summary>
    /// Период в опр. состоянии
    /// Клетка в расписании
    /// </summary>
    public class StackItem : PeriodItem
    {

        public string ContractState { get; set; }
        public int DayCount { get; set; }
        public string Hint = string.Empty;      
        public bool IsProcessedContractItemsCell    = false;        
        public int SurfaceNumber { get; set; }

    }

    #endregion 


    readonly Unit Cell_Item_Sel_h   = new Unit("20px");
    readonly Unit Cell_Item_h       = new Unit("20px");
    readonly Unit Cell_Q_h          = new Unit("3px");
    readonly Unit Cell_Q_Sel_h      = new Unit("6px");

    
    
    
    readonly Unit _100_percent          = new Unit("100%");

    readonly string Css_Contract_Cell   = "Contract_Cell";
    readonly string Css_SelCell         = "SelCell";
    readonly string Css_Q_Cell          = "Q_Cell";
    readonly string Css_Reserv_Cell     = "Reserv_Cell";
    readonly string Css_Vacant_Cell     = "Vacant_Cell";
    readonly string Css_Advice_Cell     = "Advice_Cell";
    
    
    
    
    private bool IsHead_                                    = false;
    private DateTimeTools.TimePeriod _DetailsPeriod         = DateTimeTools.TimePeriod.Day;                                      
    private SideShedule _SideShedule                        =null;
    private ContractSurfaceItemList _ProcessedContractItems = null;


    /// <summary>
    /// HTML представляющий расписания стороны  
    /// </summary>
    /// <param name="DetailsPeriod">Период детализации</param>    
    /// <param name="SideShedule">Расписание стороны в указанный период </param>
    /// <param name="ProcessedContract">Указатель на обрабатываемый контракт</param>
    /// <returns></returns>
    public static string GetHtml(DateTimeTools.TimePeriod DetailsPeriod,                                  
                                 SideShedule SideShedule,
                                 ContractSurfaceItemList  ProcessedContractItems )
    {

        SurfaceSheduleRowWriter w = new SurfaceSheduleRowWriter(DetailsPeriod, 
                                                                SideShedule, 
                                                                ProcessedContractItems);
        
        return w.GetHtml();
    }

    #region Строка заголовка
    public static string GetHeadRow(DateTimeTools.TimePeriod DetailsPeriod, SideShedule SideShedule)
    {
        SurfaceSheduleRowWriter w= new SurfaceSheduleRowWriter (DetailsPeriod,SideShedule) ;
        return w.GetHeadHtml();
    }

    private string GetHeadHtml( )
    {
        Table tbl = new Table() { Width = _100_percent, CellPadding = 0, CellSpacing = 0 };    
        TableRow rw = new TableRow();
        FillHeadRowBox(ref rw);
        tbl.Rows.Add(rw);
        return ToHtml(tbl);
    }

    private void FillHeadRowBox(ref TableRow rw)
    {
        Int16 CellSize = 100;
        CellSize = Convert.ToInt16(100 / (Diff == 0 ? 1 : Diff));
        //Формируем ячейки строчки 
        foreach (PeriodItem p in PeriodList)
        {
            TableCell Cell = new TableCell();
            switch (_DetailsPeriod)
            {
                case DateTimeTools.TimePeriod.Day:
                    Cell .Text = p.FirstDay .Day .ToString ();
                    if (DateTimeTools.GetDayDiff(_SideShedule.dtEnd, _SideShedule.dtStart ) > 20)
                        Cell.Font.Size = new FontUnit(FontSize.XXSmall );  
                    break;
                case DateTimeTools.TimePeriod.Month :
                    Cell.Text = p.FirstDay.ToString ("MMMM");
                    if (DateTimeTools.GetDayDiff(_SideShedule.dtEnd, _SideShedule.dtStart) > 100)
                        Cell.Text = p.FirstDay.ToString("MMM");
                    break;
                case DateTimeTools.TimePeriod.Week:

                    Cell.Text = p.FirstDay.Day.ToString() + "-" + p.LastDay.Day.ToString();
                    if (DateTimeTools.GetDayDiff(_SideShedule.dtEnd, _SideShedule.dtStart) > 60)
                            Cell.Font.Size = new FontUnit(FontSize.XSmall);  
                break;
                    
            }
            Cell.HorizontalAlign = HorizontalAlign.Center;
            rw.Cells.Add(Cell);
        }
    }

    #endregion 


    private string GetHtml()
    {
        Table tbl = new Table() { Width = _100_percent, CellPadding =0, CellSpacing =0};        
        int j = (_SideShedule.IHave_Q ?_SideShedule.MaxSurfValue+1 : _SideShedule.Side.SurfaceCount ); 
        for (int i = 0; i < j; i++)
        {
            TableRow rw = new TableRow();            
            FillRowBox(ref rw, i);            
            tbl.Rows.Add(rw);
        }  

        return ToHtml(tbl);
    
    }
    /// <summary>
    /// Рендеринг срочки
    /// </summary>
    /// <param name="tbl"></param>
    /// <returns></returns>
    private string ToHtml(Table tbl)
    {
        StringBuilder sb;
        MemoryStream ms = new MemoryStream();
        sb = new StringBuilder();
        StringWriter sw = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);

        System.Web.UI.Html32TextWriter w = new System.Web.UI.Html32TextWriter(sw);
        
        tbl.RenderControl(w);
        
        return sb.ToString();
    }
   
    
    /// <summary>
    /// Формиркем строчку
    /// </summary>
    /// <param name="rw"></param>
    /// <param name="SurfaceNumber"></param>
    private void FillRowBox(ref TableRow rw, int SurfaceNumber)
    {
        Int16 CellSize = 100;      
        CellSize = Convert.ToInt16(100 / (Diff == 0 ? 1 : Diff));
        //Формируем ячейки строчки 
        foreach (PeriodItem p in PeriodList)
        {
            TableCell Cell = GetCell(p, SurfaceNumber);
            rw.Cells.Add(Cell);
        }
    }

        

    /// <summary>
    /// Обрабатываемая поверхность в очереди 
    /// если номер> _SideShedule.Side.SurfaceCount-1
    /// </summary>
    /// <param name="SurfaceNumber"></param>
    /// <returns></returns>
    private bool GetIsQ(int SurfaceNumber)
    {
        //return (SurfaceNumber > (_SideShedule.MaxSurfValue == 0 ? 0 : _SideShedule.MaxSurfValue - 1) && SurfaceNumber > _SideShedule.Side.SurfaceCount-1);
        return SurfaceNumber > _SideShedule.Side.SurfaceCount - 1;
    }

    private TableCell GetCell(PeriodItem Period, int SurfaceNumber)
    {
        bool isQ = GetIsQ(SurfaceNumber);
        
        Unit H = (isQ ? Cell_Q_h : Cell_Item_h);
     
        TableCell Cell = new TableCell() { Height =  H};
        
        Cell.Controls.Add(
                            GetInsideTable(GetPeriodStack(Period, SurfaceNumber))
                          );
        return Cell;
    }

    //Содержание клетки бокса
    private Table GetInsideTable(List<StackItem> Stack)
    {

        Table tbl = new Table(){Width =_100_percent , CellSpacing =1, CellPadding =1} ;                
        TableRow rw = new TableRow();
        // Общее количество дней в периоде    
        int DayCount = 0;
        foreach (StackItem item in Stack) { DayCount = DayCount + item.DayCount; }
        int p = Convert.ToInt16(100 / DayCount);
        foreach (StackItem item in Stack)
        {
            // Формируем клетку - демонстрирующую загрузку                
            int width = Convert.ToInt16(item.DayCount * p);
            TableCell cell = GetNewCell(width, item,item.Hint );
            rw.Cells.Add(cell);
        }
        tbl.Rows.Add(rw);
        return tbl;
    }

    private TableCell GetNewCell(Unit Width, StackItem StackItem, string Hint)
    {
        bool isQ = GetIsQ(StackItem.SurfaceNumber);

        string css = string.Empty;
        switch (StackItem.ContractState)
        {
            case GlobalConstants.Contract_Contract:
                css = (isQ ? Css_Q_Cell : Css_Contract_Cell);
                break;
            case GlobalConstants.Contract_Advice:
                css = (isQ ? Css_Q_Cell : Css_Advice_Cell);
                break;
            case GlobalConstants.Contract_Reserv:
                css = (isQ ? Css_Q_Cell : Css_Reserv_Cell); 
                break;

            default:
                css = (isQ?string.Empty :Css_Vacant_Cell);
                break;
        }


        if (StackItem.IsProcessedContractItemsCell) //если день относится к обрабатываемому периоду 
                        css = css + ' ' + Css_SelCell;


        TableCell cell = new TableCell() 
                        { Width = Width, 
                         CssClass = css,                          
                         Text =  (isQ?string.Empty :"&nbsp"), 
                         ToolTip =Hint 
                        };

        if (isQ)// период  в очереди?            
            cell.Height = (StackItem.IsProcessedContractItemsCell ? Cell_Q_Sel_h : Cell_Q_h);
           // cell.Height = Cell_Q_h;
        else
            cell.Height = (StackItem.IsProcessedContractItemsCell ? Cell_Item_Sel_h : Cell_Item_h);


        if (css != Css_Contract_Cell && _ProcessedContractItems != null) //если ячейка не является контрактом и к нам пришел спсисок  цепляем на нее обработчик события OndblClick 
        {                        
            string [] Param= { _SideShedule.Side .ID,StackItem.FirstDay.ToShortDateString(),StackItem.LastDay.ToShortDateString()};            
            cell.Attributes.Add("ondblclick", string.Format("CallWS_AddSurfaceItem('{0}','{1}','{2}');", Param));
        }

        return cell;
    }

    private SurfaceSheduleRowWriter(DateTimeTools.TimePeriod DetailsPeriod,SideShedule SideShedule)
    {
        _DetailsPeriod = DetailsPeriod;
        _SideShedule = SideShedule;
    }
    private   SurfaceSheduleRowWriter   (   DateTimeTools.TimePeriod DetailsPeriod ,                                             
                                            SideShedule SideShedule, 
                                            ContractSurfaceItemList ProcessedContractItems )

    {

        _DetailsPeriod = DetailsPeriod;
        _SideShedule = SideShedule;
        _ProcessedContractItems = ProcessedContractItems;

        
     }


    /// <summary>
    /// Разница между началом и кончанием в периодах
    /// </summary>
    public int Diff
    {
        get
        {
            return DateTimeTools.GetDiff(_SideShedule.dtStart, _SideShedule.dtEnd , _DetailsPeriod);
        }
    }


    /// <summary>
    /// Набор периодов размерностью равной PeriodType
    /// </summary>
    public List<PeriodItem> PeriodList
    {
        get
        {
            // для месяцев - первый последний день месяца
            DateTime FirstDay = DateTime.Now;
            DateTime nullDay = DateTime.Now;
            DateTime LastDay = DateTime.Now;

            SetFirstLast_DayOfPeriod(_SideShedule.dtStart , ref FirstDay, ref nullDay);
            SetFirstLast_DayOfPeriod(_SideShedule.dtEnd , ref nullDay, ref LastDay);

            List<PeriodItem> l = new List<PeriodItem>();
            DateTime dt = FirstDay;
            for (int i = 0; i < Diff; i++)
            {
                PeriodItem p = new PeriodItem();
                p.FirstDay = dt;
                p.LastDay = DateTimeTools.GetLastDayOfPeriod(dt, _DetailsPeriod );
                dt = p.LastDay.AddDays(1);
                l.Add(p);
            }

            return l;
        }
    }

    /// <summary>
    /// устанавливает - ервый последний день в периода которому принадлежит дата StartDay
    /// </summary>
    /// <param name="StartDay"></param>
    /// <param name="FirstDay"></param>
    /// <param name="LastDay"></param>
    private void SetFirstLast_DayOfPeriod(DateTime StartDay, ref DateTime FirstDay, ref DateTime LastDay)
    {
        switch (_DetailsPeriod)
        {
            case DateTimeTools.TimePeriod.Month:
                FirstDay = DateTimeTools.GetFisrtDayInMonth(StartDay);
                LastDay = DateTimeTools.GetLastDayInMonth(StartDay);
                return;
            case DateTimeTools.TimePeriod.Week:
                FirstDay = DateTimeTools.GetFisrtDayInWeek(StartDay);
                LastDay = DateTimeTools.GetLastDayInWeek(StartDay);
                return;

        }

        FirstDay = StartDay;
        LastDay = StartDay;
        return;
    }

    /// <summary>
    /// Построить стэк переходов из состяние в состояние работает только с поверхностями вне очереди
    /// </summary>
    /// <returns></returns>
    public List<StackItem> GetPeriodStack(PeriodItem Period, int SurfaceNumber)
    {
        DateTime FirstDay = Period.FirstDay;
        DateTime LastDay = Period.LastDay;
        SurfaceSheduleItem SurfaceSheduleItem=null;
        //идем по дням и строим стек переходов состояний 

        List<StackItem> stack = new List<StackItem>();
        StackItem CurrentState = new StackItem();
        CurrentState.FirstDay = FirstDay;
        CurrentState.LastDay = LastDay;
        DateTime dt = FirstDay;        
        // описание периода 
        SurfaceSheduleItem = GetDayState(dt, SurfaceNumber);
        CurrentState.ContractState = (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ContractTypeID);
        CurrentState.IsProcessedContractItemsCell = IsProcessedContractItemsCell(SurfaceSheduleItem);                
        CurrentState.SurfaceNumber = SurfaceNumber;
        CurrentState.Hint = CurrentState.FirstDay.ToShortDateString() + "-" + CurrentState.LastDay.ToShortDateString();
        
        string LastSurfaceSheduleItemId = string.Empty;
        LastSurfaceSheduleItemId = (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ID);
        
        while (DateTimeTools.DayInPeriod(FirstDay, LastDay, dt))
        {
            SurfaceSheduleItem = GetDayState(dt, SurfaceNumber);
        
            string DayState = (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ContractTypeID);             
            //if (CurrentState.ContractState == DayState)
            if(LastSurfaceSheduleItemId==(null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ID))
            {
                CurrentState.DayCount++;                
                CurrentState.LastDay = dt;
            }
            else
            {
                LastSurfaceSheduleItemId = (null == SurfaceSheduleItem ? string.Empty : SurfaceSheduleItem.ID);
                stack.Add(CurrentState);
                CurrentState = new StackItem() { ContractState = DayState, FirstDay = dt, LastDay = dt, DayCount = 1 };
                CurrentState.IsProcessedContractItemsCell = IsProcessedContractItemsCell(SurfaceSheduleItem);                
                CurrentState.SurfaceNumber = SurfaceNumber;
                CurrentState.Hint = CurrentState.FirstDay.ToShortDateString() + "-" + CurrentState.LastDay.ToShortDateString();             

            }

            dt = dt.AddDays(1);
        }
        stack.Add(CurrentState);

        return stack;

    }

    /// <summary>
    /// Возврващет Id состояния поверхности для даного дня 
    /// </summary>
    /// <param name="Day"></param>
    /// <param name="SurfaceNumber"></param>
    /// <returns></returns>
    /// 

    
    /// 0 - не пересекаются
    /// 1- X входит в Y 
    /// 2- X левее Y
    /// 3- X правее Y
    /// 4- X==Y
    /// 5-X примыкает к Y 
    /// 6-Y примыкает к X
    /// 
    private SurfaceSheduleItem GetDayState(DateTime Day, int SurfaceNumber)
    {
        if (_SideShedule.Count   == 0)
            return null;

        int CrossType = 0;
        SurfaceSheduleItem i = _SideShedule.GetSurfaceSheduleItem(Day, Day, SurfaceNumber,ref CrossType);
        if (null!=i)
            return i;
        else
            return null;

    }

    /// <summary>
    /// Относится ли отрезок к редактируемому набору
    /// </summary>
    /// <param name="SurfaceSheduleItem"></param>
    /// <returns></returns>
    private bool IsProcessedContractItemsCell(SurfaceSheduleItem SurfaceSheduleItem)
    {
        if (_ProcessedContractItems == null)
                return false;

        if (_SideShedule.Count == 0)
                return false ;

        if (null == SurfaceSheduleItem)
                return false;
        
        _ProcessedContractItems.Reset();
        foreach (ContractSurfaceItem i in _ProcessedContractItems)
        { 
            if(i.ContractID ==SurfaceSheduleItem.ContractId)
            return true ;
        }
            return false;
        //string s = SurfaceSheduleItem.ID;        
        //return (null!=_ProcessedContractItems[s]);
    }

}


